2026-09-20 · tryhackme, class-notes
Note: these notes are written from the module's topic outline (data representation, Python, JavaScript, databases and SQL). Cross-check room specifics as you go.
Class notes · TryHackMe Pre Security (2026 path) · Module 4
Quick reference: the short version of this module is the Module 4 cheat sheet.
Note: these notes are written from the module's topic outline (data representation, Python, JavaScript, databases and SQL). Cross-check room specifics as you go.
By the end of these notes you should be able to:
Everything in a computer is stored as bits. What those bits mean depends on how the program interprets them.
Form
65 written as
Decimal
65
Binary
0100 0001
Hexadecimal
0x41
ASCII character
A
The byte 0x41 is "A" to a text editor, 65 to a calculator, and part of a colour or instruction to other programs. Context gives bits meaning.
Type
Holds
Python
JavaScript
Integer
Whole numbers
int — 42
number — 42
Floating point
Decimals (approximate)
float — 3.14
number — 3.14
String
Text
str — "hello"
string — "hello"
Boolean
True or false
bool — True / False
boolean — true / false
List / array
Ordered collection
list — [22, 80]
Array — [22, 80]
Dictionary / object
Key → value pairs
dict — {"port": 22}
Object — {port: 22}
Nothing
Absence of a value
None
null / undefined
Floating point is approximate. 0.1 + 0.2 gives 0.30000000000000004 in both languages, because 0.1 can't be stored exactly in binary. Money is handled in whole pence/cents or with a decimal type.
Task
Python
JavaScript
Decimal → binary
bin(10) → '0b1010'
(10).toString(2) → '1010'
Decimal → hex
hex(255) → '0xff'
(255).toString(16) → 'ff'
Binary / hex → decimal
int('1010', 2), int('ff', 16)
parseInt('1010', 2), parseInt('ff', 16)
Character → code
ord('A') → 65
'A'.charCodeAt(0) → 65
Code → character
chr(97) → 'a'
String.fromCharCode(97) → 'a'
Check type
type(x)
typeof x
Python is readable and widely used in security for automation, tooling and data analysis. Run it interactively with python3, or save code in a .py file and run python3 script.py.
name = "Andrew" \
attempts = 3 \
is_admin = False \
port = input("Port to check: ") \
port = int(port) \
print(f"Checking port {port}") \
if port == 22: print("SSH") elif port == 3389: print("RDP — should not be exposed to the internet") else: print("Other service")
Indentation is part of the syntax. The indented block belongs to the if. Comparison uses ==; a single = assigns.
Operator
Meaning
==, !=
Equal, not equal
<, >, <=, >=
Comparisons
and, or, not
Combine conditions
in
Membership: 22 in [22, 80] → True
for port in [22, 80, 443]: \
print(port)
for i in range(5): \
print(i)
tries = 0 while tries < 3: \
tries \+= 1
def is_risky(port): risky = [21, 23, 445, 3389] return port in risky
print(is_risky(23)) \
Goal: given login records, flag any user with 3 or more failures.
logins = [ {"user": "amy", "result": "fail"}, {"user": "amy", "result": "fail"}, {"user": "ben", "result": "ok"}, {"user": "amy", "result": "fail"}, {"user": "carl", "result": "fail"}, ]
failures = {} \
for entry in logins: if entry["result"] == "fail": failures[entry["user"]] = failures.get(entry["user"], 0) + 1
for user, count in failures.items(): if count >= 3: print(f"ALERT: {user} has {count} failed logins") \
How it works:
This is the same logic a SIEM brute-force rule uses.
JavaScript runs in every web browser, and on servers with Node.js. You can try it now: press F12 in a browser, open the Console tab, and type code.
let tries = 0; // let = can change const risky = [21, 23, 445, 3389]; // const = won't be reassigned
function isRisky(port) { return risky.includes(port); }
for (const port of [22, 23, 443]) { if (isRisky(port)) { console.log(`Port ${port} is risky`); // template literal } else { console.log(`Port ${port} looks fine`); } }
Aspect
Python
JavaScript
Blocks
Indentation
{ } braces
Statement end
Newline
; (optional, but conventional)
Variables
x = 5
let x = 5; / const x = 5;
Equality
==
=== strict (preferred); == converts types first
Logic
and, or, not
&&, ||, !
Output
print()
console.log()
Where it runs
Terminal, scripts, servers
Browsers, Node.js
'5' == 5 // true — JavaScript converts the string to a number first '5' === 5 // false — different types 0 == '' // true — surprising 0 === '' // falseLoose equality (==) causes subtle bugs, including security bugs such as authentication checks that compare the wrong types. Use ===.
A relational database stores data in tables of rows (records) and columns (fields). Tables relate to each other through keys.
users logins +----+-------+-------+ +----+---------+---------------------+--------+ | id | name | role | | id | user_id | time | result | +----+-------+-------+ +----+---------+---------------------+--------+ | 1 | amy | admin |<---------------| 1 | 1 | 2026-09-18 09:00:00 | fail | | 2 | ben | user |<---+ | 2 | 1 | 2026-09-18 09:01:00 | ok | | 3 | carl | user | +-----------| 3 | 2 | 2026-09-18 09:05:00 | ok | +----+-------+-------+ +----+---------+---------------------+--------+ primary key: id foreign key: user_id -> users.id
Term
Meaning
Table
A set of related records, like a spreadsheet sheet
Row / record
One entry, e.g. one user
Column / field
One attribute, e.g. name
Primary key
Uniquely identifies each row (users.id)
Foreign key
A column that references another table's primary key (logins.user_id)
Schema
The structure: tables, columns, types, keys
Relational (SQL)
NoSQL
Fixed tables and relationships
Documents, key–value pairs, graphs
MySQL, PostgreSQL, SQL Server, SQLite
MongoDB, Redis
Strong consistency; joins
Flexible schemas; horizontal scale
SQL (Structured Query Language) is how you talk to relational databases. The core operations are known as CRUD: create, read, update, delete.
CREATE TABLE users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, role TEXT );
INSERT INTO users (name, role) VALUES ('amy', 'admin'); INSERT INTO users (name, role) VALUES ('ben', 'user'); INSERT INTO users (name, role) VALUES ('carl', 'user');
SELECT * FROM users; -- every column, every row SELECT name FROM users WHERE role = 'user'; -- filter SELECT name FROM users WHERE name LIKE 'a%'; -- starts with a (% = any characters) SELECT name FROM users ORDER BY name DESC; -- sort SELECT COUNT(*) FROM users WHERE role = 'admin'; -- count SELECT * FROM users LIMIT 2; -- first 2 rows
Worked example — query results. With the three users above:
Query
Result
SELECT name FROM users WHERE role = 'user';
ben, carl
SELECT COUNT(*) FROM users;
3
SELECT name FROM users WHERE role = 'admin' OR name = 'carl';
amy, carl
UPDATE users SET role = 'admin' WHERE name = 'ben'; DELETE FROM users WHERE name = 'carl';
Caution: UPDATE or DELETE without a WHERE changes or removes every row. Write the WHERE first, and test it as a SELECT before running the change.
SELECT u.name, l.time, l.result FROM logins l JOIN users u ON l.user_id = u.id WHERE l.result = 'fail';This matches each login to its user through the foreign key, then filters to failures. In the example data it returns amy | 2026-09-18 09:00:00 | fail.
SQLite is a whole database in a single file — no server needed.
sqlite3 lab.db
Command inside sqlite3
Does
.tables
List tables
.schema users
Show a table's structure
.headers on then .mode column
Readable output
.quit
Exit
SQL injection happens when user input is pasted directly into an SQL statement, so the input can change the query itself.
Vulnerable login code:
username = input("User: ") password = input("Password: ") query = "SELECT * FROM users WHERE name = '" + username + "' AND password = '" + password + "'"
Normal use: the user types amy and secret, giving:
SELECT * FROM users WHERE name = 'amy' AND password = 'secret'
The attacker types the username admin' -- and anything as the password. The query becomes:
SELECT * FROM users WHERE name = 'admin' --' AND password = 'anything'1. The attacker's ' closes the string early. 2. -- starts an SQL comment, so the password check is ignored. 3. The query now just means "find the admin user", so the attacker logs in as admin without a password.
Other payloads can dump whole tables, or change or delete data. SQL injection has been behind many major breaches.
Never build queries by gluing strings together. Use parameters, so the database treats input purely as data:
import sqlite3 conn = sqlite3.connect("lab.db") cur = conn.cursor() cur.execute("SELECT * FROM users WHERE name = ? AND password = ?", (username, password))
The ? placeholders are filled safely. An input of admin' -- is searched for literally as a username (which doesn't exist) — it can't change the query's structure.
Term
Definition
Data type
The kind of value: integer, float, string, boolean, etc.
Variable
A named storage location for a value
String
A sequence of characters
Boolean
A true/false value
Floating point
An approximate representation of decimal numbers
Condition
A test that decides which code runs (if)
Loop
Code that repeats (for, while)
Function
A named, reusable block of code
List / array
An ordered collection of values
Dictionary / object
A collection of key → value pairs
Type coercion
Automatic conversion between types (JavaScript ==)
Relational database
A database of related tables
Primary key
A column that uniquely identifies each row
Foreign key
A column referencing another table's primary key
SQL
The language for querying relational databases
CRUD
Create, read, update, delete
JOIN
Combining rows from two tables on a related column
SQL injection
An attack where input alters an SQL query's structure
Parameterised query
A query with placeholders filled safely by the database driver
Hashing (passwords)
One-way transformation used to store passwords safely